package com.example.smartwaterapi.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * JWT工具类，用于生成和解析JWT令牌
 */
@Slf4j
@Component
public class JwtTokenUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private long expiration;

    @Value("${jwt.token-head}")
    private String tokenHead;

    /**
     * 从token中提取用户名
     */
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    /**
     * 获取token的过期时�?
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    /**
     * 从token中提取特定信�?
     */
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 从token中获取所有信�?
     */
    private Claims getAllClaimsFromToken(String token) {
        Key key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 检查token是否已过�?
     */
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * 从token中获取最后活跃时�?
     */
    public Date getLastActiveTimeFromToken(String token) {
        try {
            return getClaimFromToken(token, claims -> {
                Object lastActiveTime = claims.get("lastActiveTime");
                if (lastActiveTime instanceof Long) {
                    return new Date((Long) lastActiveTime);
                }
                return null;
            });
        } catch (Exception e) {
            log.warn("无法从token中获取最后活跃时�? {}", e.getMessage());
            return null;
        }
    }

    /**
     * 检查token是否因不活跃而过期（30天未使用）
     */
    private Boolean isTokenInactivityExpired(String token) {
        try {
            Date lastActiveTime = getLastActiveTimeFromToken(token);
            if (lastActiveTime == null) {
                // 如果没有lastActiveTime，使用token的签发时�?
                Date issuedAt = getClaimFromToken(token, Claims::getIssuedAt);
                lastActiveTime = issuedAt != null ? issuedAt : new Date();
            }
            
            // 检查是否超�?天未活跃�? * 24 * 60 * 60 * 1000 = 604800000毫秒�?
            long inactivityExpireTime = 30 * 24 * 60 * 60 * 1000L;
            long timeSinceLastActive = System.currentTimeMillis() - lastActiveTime.getTime();
            
            boolean expired = timeSinceLastActive > inactivityExpireTime;
            if (expired) {
                log.debug("Token因不活跃过期: lastActiveTime={}, timeSinceLastActive={}ms, expireTime={}ms", 
                         lastActiveTime, timeSinceLastActive, inactivityExpireTime);
            }
            
            return expired;
        } catch (Exception e) {
            log.warn("检查token不活跃过期失�? {}", e.getMessage());
            return false;
        }
    }

    /**
     * 生成token
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        
        // 添加最后活跃时�?
        claims.put("lastActiveTime", System.currentTimeMillis());
        
        // 如果是SecurityUser，使用用户ID作为subject
        if (userDetails instanceof SecurityUser) {
            SecurityUser securityUser = (SecurityUser) userDetails;
            String userId = String.valueOf(securityUser.getUserId());
            claims.put("username", userDetails.getUsername());
            return doGenerateToken(claims, userId);
        }
        
        // 否则使用用户名作为subject
        return doGenerateToken(claims, userDetails.getUsername());
    }

    /**
     * 生成token（自定义声明�?
     */
    public String generateToken(String subject, Map<String, Object> claims) {
        if (claims == null) {
            claims = new HashMap<>();
        }
        return doGenerateToken(claims, subject);
    }

    /**
     * 生成token的核心方�?
     */
    private String doGenerateToken(Map<String, Object> claims, String subject) {
        final Date createdDate = new Date();
        final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000);

        Key key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
        
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(createdDate)
                .setExpiration(expirationDate)
                .signWith(key)
                .compact();
    }

    /**
     * 验证token是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        try {
            // 从token中提取subject，应该是用户ID
            final String subject = getUsernameFromToken(token);
            
            // 如果是SecurityUser，检查token的subject是否匹配用户ID
            if (userDetails instanceof SecurityUser) {
                SecurityUser securityUser = (SecurityUser) userDetails;
                String userId = String.valueOf(securityUser.getUserId());
                
                // 记录日志以便调试
                log.debug("验证令牌: token subject={}, userId={}", subject, userId);
                
                // 如果用户ID为null，则尝试使用用户名进行验�?
                if (securityUser.getUserId() == null) {
                    log.warn("用户ID为null，尝试使用用户名验证令牌");
                    
                    // 从token中获取username claim
                    String tokenUsername = null;
                    try {
                        tokenUsername = getClaimFromToken(token, claims -> claims.get("username", String.class));
                    } catch (Exception e) {
                        log.error("无法从token中提取username: {}", e.getMessage());
                    }
                    
                    // 如果token中有username，与用户的openid比较
                    if (tokenUsername != null) {
                        boolean tokenExpired = isTokenExpired(token);
                        boolean inactivityExpired = isTokenInactivityExpired(token);
                        boolean valid = (tokenUsername.equals(userDetails.getUsername()) && !tokenExpired && !inactivityExpired);
                        
                        log.debug("使用openid验证令牌: token username={}, openid={}, tokenExpired={}, inactivityExpired={}, 结果={}", 
                                tokenUsername, userDetails.getUsername(), tokenExpired, inactivityExpired, valid);
                        return valid;
                    }
                }
                
                // 检查token是否过期以及subject是否匹配用户ID
                boolean tokenExpired = isTokenExpired(token);
                boolean inactivityExpired = isTokenInactivityExpired(token);
                boolean valid = (subject.equals(userId) && !tokenExpired && !inactivityExpired);
                
                if (!valid) {
                    log.warn("令牌验证失败: subject={}, userId={}, tokenExpired={}, inactivityExpired={}", 
                             subject, userId, tokenExpired, inactivityExpired);
                }
                return valid;
            }
            
            // 否则检查token的subject是否匹配用户�?
            boolean tokenExpired = isTokenExpired(token);
            boolean inactivityExpired = isTokenInactivityExpired(token);
            boolean valid = (subject.equals(userDetails.getUsername()) && !tokenExpired && !inactivityExpired);
            
            if (!valid) {
                log.warn("令牌验证失败: subject={}, username={}, tokenExpired={}, inactivityExpired={}", 
                         subject, userDetails.getUsername(), tokenExpired, inactivityExpired);
            }
            return valid;
        } catch (Exception e) {
            log.error("令牌验证异常: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 刷新Token的活跃时间（保持其他信息不变�?
     */
    public String refreshTokenActiveTime(String token) {
        try {
            // 获取原Token的所有claims
            Claims claims = getAllClaimsFromToken(token);
            String subject = claims.getSubject();
            
            // 更新最后活跃时�?
            claims.put("lastActiveTime", System.currentTimeMillis());
            
            // 保持原有的过期时�?
            Date originalExpiration = claims.getExpiration();
            
            // 重新生成Token
            Key key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
            
            return Jwts.builder()
                    .setClaims(claims)
                    .setSubject(subject)
                    .setIssuedAt(claims.getIssuedAt()) // 保持原签发时�?
                    .setExpiration(originalExpiration) // 保持原过期时�?
                    .signWith(key)
                    .compact();
        } catch (Exception e) {
            log.error("刷新Token活跃时间失败: {}", e.getMessage());
            return token; // 如果刷新失败，返回原Token
        }
    }

    /**
     * 检查Token是否需要刷新活跃时间（超过1小时未更新）
     */
    public boolean shouldRefreshActiveTime(String token) {
        try {
            Date lastActiveTime = getLastActiveTimeFromToken(token);
            if (lastActiveTime == null) {
                return true; // 如果没有活跃时间，需要添�?
            }
            
            // 如果超过1小时未更新活跃时间，则需要刷�?
            long timeSinceLastUpdate = System.currentTimeMillis() - lastActiveTime.getTime();
            long oneHour = 60 * 60 * 1000L;
            
            return timeSinceLastUpdate > oneHour;
        } catch (Exception e) {
            log.warn("检查Token活跃时间刷新需求失�? {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取token前缀
     */
    public String getTokenHead() {
        return tokenHead;
    }
}

